package com.resurvolution.kingcloudmusicspringboot.product.service.serviceImpl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.resurvolution.kingcloudmusicspringboot.common.constant.SongConstant;
import com.resurvolution.kingcloudmusicspringboot.common.exception.FileException;
import com.resurvolution.kingcloudmusicspringboot.product.dto.ArtistDTO;
import com.resurvolution.kingcloudmusicspringboot.product.service.GenreService;
import com.resurvolution.kingcloudmusicspringboot.product.vo.*;
import com.resurvolution.kingcloudmusicspringboot.product.dto.SongsDTO;
import com.resurvolution.kingcloudmusicspringboot.product.entity.SongsEntity;
import com.resurvolution.kingcloudmusicspringboot.product.mapper.SongsMapper;
import com.resurvolution.kingcloudmusicspringboot.product.service.ArtistService;
import com.resurvolution.kingcloudmusicspringboot.product.service.SongsService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class SongsServiceImpl extends ServiceImpl<SongsMapper, SongsEntity> implements SongsService {

    @Value("${file.upload-path}")
    private String uploadPath;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private ArtistService artistService;
    @Resource
    private GenreService genreService;
    @Resource
    private SongsMapper songsMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadSongs(MultipartFile song,
                            MultipartFile lyric,
                            MultipartFile tinyImg,
                            MultipartFile largeImg,
                            String songDataJson) throws IOException, UnsupportedAudioFileException {

        File dir = new File(uploadPath);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new FileException();
            }
        }

        SongsDTO songsDTO = objectMapper.readValue(songDataJson, SongsDTO.class);
        SongsEntity songsEntity = new SongsEntity();

        if (song != null) {
            String type = song.getOriginalFilename().substring(song.getOriginalFilename().lastIndexOf('.'));
            Path uploadDir = Paths.get(uploadPath);
            String sourcePath = RandomStringUtils.random(32, song.getOriginalFilename()) + type;
            Path destPath = uploadDir
                    .resolve("songs")
                    .resolve(sourcePath);
            destPath.getParent().toFile().mkdirs();
            song.transferTo(destPath);
            String relativePath = destPath.toString().substring(destPath.toString().lastIndexOf("songs") - 1);
            songsDTO.setFileUrl(relativePath);
            songsEntity.setFileUrl(songsDTO.getFileUrl());
        }
        if (lyric != null) {
            String type = lyric.getOriginalFilename().substring(lyric.getOriginalFilename().lastIndexOf('.'));
            Path uploadDir = Paths.get(uploadPath);
            String sourcePath = RandomStringUtils.random(32, lyric.getOriginalFilename()) + type;
            Path destPath = uploadDir
                    .resolve("lyrics")
                    .resolve(sourcePath);
            destPath.getParent().toFile().mkdirs();
            lyric.transferTo(destPath);
            String relativePath = destPath.toString().substring(destPath.toString().lastIndexOf("lyrics") - 1);
            songsDTO.setLyric(relativePath);
            songsEntity.setLyric(songsDTO.getLyric());
        }
        if (tinyImg != null) {
            String type = tinyImg.getOriginalFilename().substring(tinyImg.getOriginalFilename().lastIndexOf('.'));
            Path uploadDir = Paths.get(uploadPath);
            String sourcePath = RandomStringUtils.random(32, tinyImg.getOriginalFilename()) + type;
            Path destPath = uploadDir
                    .resolve("tinyImgs")
                    .resolve(sourcePath);
            destPath.getParent().toFile().mkdirs();
            tinyImg.transferTo(destPath);
            String relativePath = destPath.toString().substring(destPath.toString().lastIndexOf("tinyImgs") - 1);
            songsDTO.setTinyImg(relativePath);
            songsEntity.setTinyImg(songsDTO.getTinyImg());
        }
        if (largeImg != null) {
            String type = largeImg.getOriginalFilename().substring(largeImg.getOriginalFilename().lastIndexOf('.'));
            Path uploadDir = Paths.get(uploadPath);
            String sourcePath = RandomStringUtils.random(32, largeImg.getOriginalFilename()) + type;
            Path destPath = uploadDir
                    .resolve("largeImgs")
                    .resolve(sourcePath);
            destPath.getParent().toFile().mkdirs();
            largeImg.transferTo(destPath);
            String relativePath = destPath.toString().substring(destPath.toString().lastIndexOf("largeImgs") - 1);
            songsDTO.setLargeImg(relativePath);
            songsEntity.setLargeImg(songsDTO.getLargeImg());
        }

        if (songsDTO.getGenre() == null) {
            songsEntity.setGenreId(SongConstant.NO_GENRE);
        } else {
            songsEntity.setGenreId(songsDTO.getGenre());
        }

        songsEntity.setName(songsDTO.getName());
        songsEntity.setCount(0);
        songsEntity.setCreatedAt(LocalDateTime.now());

        if (songsDTO.getArtist() != null) {
            ArtistDTO artistDTO = new ArtistDTO();
            artistDTO.setName(songsDTO.getArtist());
            artistDTO.setBio(songsDTO.getBio());
            Integer artistId = artistService.addArtist(artistDTO);
            songsEntity.setArtistId(artistId);
        } else {
            songsEntity.setArtistId(SongConstant.NO_ARTIST);
        }

        songsMapper.insertOrUpdate(songsEntity);
    }

    @Override
    public List<CarouselsVO> getCarouselsList() {
        QueryWrapper<SongsEntity> queryWrapper = new QueryWrapper<>();
        return songsMapper.selectList(queryWrapper)
                .stream()
                .sorted(Comparator.comparing(SongsEntity::getCount).reversed())
                .limit(4)
                .map(songsEntity -> {
                    CarouselsVO carouselsVO = new CarouselsVO();
                    carouselsVO.setId(songsEntity.getId());
                    carouselsVO.setImgUrl(songsEntity.getLargeImg());
                    return carouselsVO;
                })
                .collect(Collectors.toList());
    }

    @Override
    public SongsVO getSongById(Integer id) {
        QueryWrapper<SongsEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("song_id", id);
        SongsEntity songsEntity = songsMapper.selectOne(queryWrapper);
        SongsVO songsVO = new SongsVO();
        songsVO.setId(songsEntity.getId());
        songsVO.setName(songsEntity.getName());
        songsVO.setArtist(artistService.getArtistById(songsEntity.getArtistId()));
        songsVO.setCount(songsEntity.getCount());
        songsVO.setGenre(genreService.getGenreById(songsEntity.getGenreId()));
        songsVO.setFileUrl(songsEntity.getFileUrl());
        songsVO.setCreatedAt(songsEntity.getCreatedAt().toString());
        songsVO.setTinyImg(songsEntity.getTinyImg());
        songsVO.setLyric(songsEntity.getLyric());
        return songsVO;
    }

    @Override
    public List<RecommendSongsVO> getRecommendSongList(Integer id) {
        QueryWrapper<SongsEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("song_id", id);
        SongsEntity songsEntity = songsMapper.selectOne(queryWrapper);
        if (songsEntity != null) {
            artistService.getArtistById(songsEntity.getArtistId());
            genreService.getGenreById(songsEntity.getGenreId());
            queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("genre_id", songsEntity.getGenreId())
                    .or()
                    .eq("artist_id", songsEntity.getArtistId());
            return songsMapper.selectList(queryWrapper)
                    .stream()
                    .filter(songEntity -> !songEntity.getId().equals(id))
                    .map(songEntity -> {
                        RecommendSongsVO recommendSongsVO = new RecommendSongsVO();
                        recommendSongsVO.setId(songEntity.getId());
                        recommendSongsVO.setImgUrl(songEntity.getLargeImg());
                        recommendSongsVO.setName(songEntity.getName());
                        recommendSongsVO.setArtist(artistService.getArtistById(songEntity.getArtistId()));
                        return recommendSongsVO;
                    })
                    .collect(Collectors.toList());
        }
        throw new IllegalArgumentException("歌曲不存在");
    }

    @Override
    public PageVO<SongsPageVO> getPage(Integer page, Integer size, String sortBy, String sortOrder, Integer genreId) {
        IPage<SongsEntity> iPage = new Page<>(page, size);
        QueryWrapper<SongsEntity> queryWrapper = new QueryWrapper<>();
        if ("asc".equalsIgnoreCase(sortOrder)) {
            queryWrapper.orderByAsc(sortBy);
        } else if ("desc".equalsIgnoreCase(sortOrder)) {
            queryWrapper.orderByDesc(sortBy);
        } else {
            queryWrapper.orderByAsc("song_id");
        }
        if (genreId != null && genreId != -1) {
            queryWrapper.eq("genre_id", genreId);
        }
        IPage<SongsEntity> iPageResult = songsMapper.selectPage(iPage, queryWrapper);
        PageVO<SongsPageVO> pageVO = new PageVO<>();
        pageVO.setRecords(iPageResult.getRecords().stream().map((songsEntity) -> {
            SongsPageVO songsPageVO = new SongsPageVO();
            songsPageVO.setId(songsEntity.getId());
            songsPageVO.setName(songsEntity.getName());
            songsPageVO.setImgUrl(songsEntity.getTinyImg());
            songsPageVO.setArtist(artistService.getArtistById(songsEntity.getArtistId()));
            songsPageVO.setGenre(genreService.getGenreById(songsEntity.getGenreId()));
            return  songsPageVO;
        }).collect(Collectors.toList()));
        pageVO.setTotal(iPageResult.getTotal());
        return pageVO;
    }
}
